package bg.btanev.eightyfour.gwtfacade.commandhandler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;

import bg.btanev.eightyfour.domain.Tag;
import bg.btanev.eightyfour.domain.Target;
import bg.btanev.eightyfour.domain.profile.Profile;
import bg.btanev.eightyfour.domain.profile.ProfileEvent;
import bg.btanev.eightyfour.gwtfacade.converter.DtoConverter;
import bg.btanev.eightyfour.service.ProfileService;
import bg.btanev.eightyfour.service.TargetService;
import bg.btanev.eightyfour.shared.command.CommandResult;
import bg.btanev.eightyfour.shared.command.stat.StatCommandInput;
import bg.btanev.eightyfour.shared.command.stat.StatSummaryCommand;
import bg.btanev.eightyfour.shared.command.stat.StatSummaryCommandResult;
import bg.btanev.eightyfour.shared.dto.TagDto;
import bg.btanev.eightyfour.shared.dto.TargetDto;
import bg.btanev.eightyfour.shared.dto.profile.ProfileDto;
import bg.btanev.eightyfour.shared.dto.profile.ProfileEventDto;

/**
 *
 * @author B.Tanev
 *
 */
public class StatsSummaryCommandHandler
		extends
		GenericCommandHandler<StatCommandInput, StatSummaryCommandResult, CommandResult<StatCommandInput, StatSummaryCommandResult>, StatSummaryCommand> {

	private final TargetService targetService;
	private final ProfileService profileService;
	private final DtoConverter converter;

	public StatsSummaryCommandHandler(TargetService targetService, ProfileService profileService, DtoConverter converter) {
		super();

		Validate.notNull(targetService);
		Validate.notNull(profileService);
		Validate.notNull(converter);

		this.targetService = targetService;
		this.profileService = profileService;
		this.converter = converter;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected StatSummaryCommandResult doExecuteInternal(StatCommandInput attachedInput) {
		Validate.notNull(attachedInput);

		TargetDto targetInput = attachedInput.getTargetDto();
		Validate.notNull(targetInput);

		Long targetId = targetInput.getId();
		Validate.notNull(targetId);
		Validate.isTrue(targetId.longValue() > 0);

		StatSummaryCommandResult result = null;
		Target target = targetService.findByIdentifier(targetId);
		if (target != null) {
			result = new StatSummaryCommandResult();

			List<Profile<ProfileEvent>> profiles = profileService.findAllProfiles(target);
			if (profiles != null) {
				result.setTotalProfilesCount(profiles.size());
			}

			if (profiles.size() > 0) {
				List<ProfileEvent> profileEvents = profileService.findProfileEventsForTarget(target);
				if (profileEvents != null) {
					result.setTotalProfileEventsCount(profileEvents.size());

					Map<Profile, Long> profilesUseage = new HashMap<Profile, Long>(profiles.size());
					Map<Tag, Long> tagsUseage = new HashMap<Tag, Long>();

					Long profileEventsWithTags = 0L;
					for (ProfileEvent profileEvent : profileEvents) {
						Profile profile = profileEvent.getProfile();
						Set<Tag> tags = profileEvent.getTags();

						Long count = profilesUseage.get(profile);
						if (count == null) {
							count = 0L;
						}
						count += 1L;

						profilesUseage.put(profile, count);


						if (tags != null && tags.size() > 0) {
							profileEventsWithTags += 1L;
							for (Tag tag : tags) {
								count = tagsUseage.get(tag);
								if (count == null) {
									count = 0L;
								}
								count += 1L;

								tagsUseage.put(tag, count);
							}
						}
					}
					result.setTotalTaggedProfileEvents(profileEventsWithTags.longValue());

					Profile mostCommonProfile = null;
					long maxCount = 0;
					Set<Profile> profilesKeys = profilesUseage.keySet();
					for (Profile profile : profilesKeys) {
						Long currCount = profilesUseage.get(profile);
						if (currCount > maxCount) {
							maxCount = currCount;
							mostCommonProfile = profile;
						}
					}

					// convert
					result.setMostActiveProfile((ProfileDto<ProfileEventDto>) converter.domainToDto(mostCommonProfile,
							0));
					result.setProfileEventsCount(maxCount);

					Tag mostCommonTag = null;
					long mostCommonTagUseage = 0;
					long totalTagsUsed = 0;
					Set<Tag> tagsKeys = tagsUseage.keySet();
					for (Tag tag : tagsKeys) {
						Long currCount = tagsUseage.get(tag);
						if (currCount > mostCommonTagUseage) {
							mostCommonTagUseage = currCount;
							mostCommonTag = tag;
						}
						totalTagsUsed += currCount;
					}

					result.setMostCommonTagProfilesEventsCount(mostCommonTagUseage);
					result.setTotalTagsUsed(totalTagsUsed);
					// convert
					result.setMostCommonTag((TagDto) converter.domainToDto(mostCommonTag, 0));
				}

				List<Tag> uniqueTags = targetService.findTagsForTarget(target);
				if (uniqueTags != null) {
					result.setTotalUniqueTagsUsed(uniqueTags.size());
				}
			}
		}

		return result;
	}

	@Override
	protected CommandResult<StatCommandInput, StatSummaryCommandResult> packCommandResult(StatCommandInput input,
			StatSummaryCommandResult detachedResult) {

		return new CommandResult<StatCommandInput, StatSummaryCommandResult>(input, detachedResult);
	}

	@Override
	public StatCommandInput attach(StatCommandInput input) {
		return input;
	}

	@Override
	public StatSummaryCommandResult detach(StatSummaryCommandResult result) {
		return result;
	}
}
